package hk.polyu;

import java.io.*;
import java.util.*;
import java.util.Map.*;

/**
 * This class is the inverted index for all phrases.
 * @author hezongjian
 *
 */
public class InvertedIndex implements Index {
	/**
	 * Constant string storing file name
	 */
	public static final String FILE_NAME = "index.txt";
	
	/**
	 * It is a hash table of hash table. 
	 * The index of the outer hashtable is the term. 
	 * The index of the inner hashtable is the document ID.
	 */
	TreeMap<String, HashMap<Integer, Vector<Integer>>> mTokens;

	/**
	 <code>
	 	#keywords, documentID, position 1, position 2...
		chrome,2,1
		code,1,6
		explorer,2,3
		firefox,2,0
		hello,0,3
		hello,1,4
		internet,2,2
		is,0,1
		java,1,2,5
		python,0,2
		safari,2,4
		test,0,5
		that,1,0
		this,0,0
		was,1,1
		world,0,4
		world,1,3
	 </code>
	 */
	@Override
	public void save(String path) throws IOException {
		if (! path.endsWith(File.separator))
			path += File.separator;
		String fullName = path + InvertedIndex.FILE_NAME;
		
		PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(fullName)));
		out.println("#keywords, documentID, position 1, position 2...");
		// print them out.
		Iterator<String> i = mTokens.keySet().iterator();
		HashMap<Integer, Vector<Integer>> docList;
		
		while (i.hasNext()) {
			String k = i.next();
			docList = mTokens.get(k);
			Iterator<Integer> j = docList.keySet().iterator();
			
			while(j.hasNext()) {
				int id = j.next().intValue();
				Vector<Integer> poses = docList.get(id);
				// we are ready now.
				out.printf("%s, %d", k, id);
				for (Integer pos : poses) {
					out.print(", ");
					out.print(pos);
				}
				out.println();
			} // inner iteration
		} // outer iteration
		out.close();
	}

	@Override
	public void load(String path) throws IOException {
		String line;
		BufferedReader input =  new BufferedReader(new FileReader(path));
		
		this.mTokens = new TreeMap<String, HashMap<Integer,Vector<Integer>>>();
		
		while ((line = input.readLine()) != null) {
			if ( line.startsWith("#") )
				continue;
			HashMap<Integer, Vector<Integer>> map = new HashMap<Integer, Vector<Integer>>();
			
			// Separate into lines
			String [] elems = line.split(",");
			// first one, always term
			String term = elems[0].trim();
			// second one, always docID
			int docID = Integer.parseInt(elems[1].trim());
			// parse the doc position.
			Vector<Integer> vec = new Vector<Integer>();
			for(int i = 2; i < elems.length; i++) {
				int nPos = Integer.parseInt(elems[i].trim());
				vec.add(nPos);
			}
			// put to the hashmap.
			if (this.mTokens.containsKey(term)) {
				this.mTokens.get(term).put(docID, vec);
			}
			else {
				map.put(docID, vec);
				this.mTokens.put(term, map);
			}
	    }
		
		input.close();
	}

	@Override
	public void generate(CollectionInfo ci) {
		
		this.mTokens = new TreeMap<String, HashMap<Integer, Vector<Integer>>>();
		
		// merge all DocumentInfo
		Iterator<Entry<Integer, DocumentInfo>> it = ci.mDocs.entrySet().iterator();
		while (it.hasNext()) {
			Entry<Integer, DocumentInfo> pairs = it.next();
			int docID = pairs.getKey().intValue();

			// traverse the tokens in DocumentInfo
			DocumentInfo doc = pairs.getValue();
			Iterator<Entry<String, Vector<Integer>>> dit = doc.mTokens
					.entrySet().iterator();
			while (dit.hasNext()) {
				Entry<String, Vector<Integer>> tokenPair = dit.next();
				String token = tokenPair.getKey();

				if (mTokens.containsKey(token)) {
					mTokens.get(token).put(docID, tokenPair.getValue());
				} else { // do not have the token, need to create inner hash
							// table.
					HashMap<Integer, Vector<Integer>> h = new HashMap<Integer, Vector<Integer>>();
					h.put(docID, tokenPair.getValue());
					mTokens.put(token, h);
				}
			} // end inner iteration
		} // end outer iteration
	}

}
